{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -q pyjwt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests, jwt\n",
    "from IPython.display import JSON\n",
    "\n",
    "CATALOG_URL = \"http://lakekeeper:8181/catalog\"\n",
    "MANAGEMENT_URL = \"http://lakekeeper:8181/management\"\n",
    "KEYCLOAK_TOKEN_URL = \"http://keycloak:8080/realms/iceberg/protocol/openid-connect/token\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Bootstraping Lakekeeper\n",
    "This Notebook performs bootstrapping via python requests. It only works if the server hasn't previously bootstrapped using the UI!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Sign in\n",
    "First, we need to obtain a token from our Identity Provider. In this example a `Keycloak` is running beside Lakekeeper. A few users have been pre-created in Keycloak for this example. We are now logging into Keycloak as the technical user (client) `trino`. If a human user bootstraps the catalog, we recommend to use the UI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Login to Keycloak\n",
    "CLIENT_ID = \"trino\"\n",
    "CLIENT_SECRET = \"AK48QgaKsqdEpP9PomRJw7l2T7qWGHdZ\"\n",
    "\n",
    "response = requests.post(\n",
    "    url=KEYCLOAK_TOKEN_URL,\n",
    "    data={\n",
    "        \"grant_type\": \"client_credentials\",\n",
    "        \"client_id\": CLIENT_ID,\n",
    "        \"client_secret\": CLIENT_SECRET,\n",
    "        \"scope\": \"lakekeeper\"\n",
    "    },\n",
    "    headers={\"Content-type\": \"application/x-www-form-urlencoded\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "access_token = response.json()['access_token']\n",
    "\n",
    "# Lets inspect the token we got to see that our application name is available:\n",
    "JSON(jwt.decode(access_token, options={\"verify_signature\": False}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have the access token, we can query the server info Endpoint. \n",
    "On first launch it will show bootstrapped `'bootstrapped': false`.\n",
    "The full API documentation is available as part of the Repository and hosted by Lakekeeper: http://localhost:8181/swagger-ui/#/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = requests.get(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/info\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "JSON(response.json())\n",
    "# On first launch it shows \"bootstrapped\": False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Bootstrap"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = requests.post(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/bootstrap\",\n",
    "    headers={\n",
    "        \"Authorization\": f\"Bearer {access_token}\"\n",
    "    },\n",
    "    json={\n",
    "        \"accept-terms-of-use\": True,\n",
    "    },\n",
    ")\n",
    "response.raise_for_status()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Grant access to UI User\n",
    "In keycloak the user \"Peter\" exists which we are now also assigning the \"admin\" role.\n",
    "\n",
    "Before executing the next cell, login to the UI at http://localhost:8181 using:\n",
    "* Username: `peter`\n",
    "* Password: `iceberg`\n",
    "\n",
    "You should see \"You don't have any projects assignments\".\n",
    "\n",
    "Lets assign permissions to peter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Users will show up in the /v1/user endpoint after the first login via the UI\n",
    "# or the first call to the /catalog/v1/config endpoint.\n",
    "response = requests.get(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/user\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "JSON(response.json())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "response = requests.post(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/permissions/server/assignments\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    "    json={\n",
    "      \"writes\": [\n",
    "        {\n",
    "          \"type\": \"admin\",\n",
    "          \"user\": \"oidc~cfb55bf6-fcbb-4a1e-bfec-30c6649b52f8\"\n",
    "        }\n",
    "      ]\n",
    "    }\n",
    ")\n",
    "response.raise_for_status()\n",
    "\n",
    "response = requests.post(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/permissions/project/assignments\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    "    json={\n",
    "      \"writes\": [\n",
    "        {\n",
    "          \"type\": \"project_admin\",\n",
    "          \"user\": \"oidc~cfb55bf6-fcbb-4a1e-bfec-30c6649b52f8\"\n",
    "        }\n",
    "      ]\n",
    "    }\n",
    ")\n",
    "response.raise_for_status()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can now refresh the UI page and should see the default Lakehouse."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The server is now bootstrapped:\n",
    "response = requests.get(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/info\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "JSON(response.json())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# An initial user was created\n",
    "response = requests.get(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/user\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "JSON(response.json())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The \"admin\" role has been assigned:\n",
    "response = requests.get(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/permissions/server/assignments\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "user_id = response.json()['assignments'][0]['user']\n",
    "JSON(response.json())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This user is the global admin, which has all access rights to the server:\n",
    "response = requests.get(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/server/actions\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "JSON(response.json())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Lets see who this user is:\n",
    "response = requests.get(\n",
    "    url=f\"{MANAGEMENT_URL}/v1/user/{user_id}\",\n",
    "    headers={\"Authorization\": f\"Bearer {access_token}\"},\n",
    ")\n",
    "response.raise_for_status()\n",
    "JSON(response.json())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
